package com.project.common.data.mybatis.provider;

import com.project.common.data.mybatis.metadata.TableColumn;
import com.project.common.data.mybatis.metadata.TableMetadata;
import com.project.common.data.mybatis.provider.utils.ProviderUtils;
import org.apache.ibatis.builder.annotation.ProviderContext;
import org.apache.ibatis.jdbc.SQL;

import java.util.function.Predicate;
import java.util.stream.Stream;

public interface CRUDProvider extends StatementProvider {

    /**
     * 生成 Insert 语句
     */
    default String insert(ProviderContext context) {
        Class<?> clazz = this.getPersistenceType(context);
        TableMetadata tableMetadata = TableMetadata.forClass(clazz);
        return new SQL()
                .INSERT_INTO(tableMetadata.getTableName())
                .INTO_COLUMNS(Stream.of(tableMetadata.getColumns())
                        .map(TableColumn::getName)
                        .toArray(String[]::new))
                .INTO_VALUES(Stream.of(tableMetadata.getColumns())
                        .map(ProviderUtils::buildParameter)
                        .toArray(String[]::new))
                .toString();
    }

    /**
     * 生成 Update 语句
     */
    default String update(ProviderContext context) {
        return this.doUpdate(null, context, false);
    }

    /**
     * 根据持久化对象生成 Update 语句
     */
    default String updateSelective(Object entity, ProviderContext context) {
        return this.doUpdate(entity, context, true);
    }

    default String doUpdate(Object entity, ProviderContext context, boolean selective) {
        Class<?> clazz = getPersistenceType(context);
        TableMetadata tableMetadata = TableMetadata.forClass(clazz);
        Predicate<TableColumn> predicate = !selective ?
                (tableColumn -> !tableColumn.isKey()) :
                (tableColumn -> !tableColumn.isKey() && ProviderUtils.checkField(tableColumn, clazz, entity));
        return new SQL()
                .UPDATE(tableMetadata.getTableName())
                .SET(Stream.of(tableMetadata.getColumns())
                        .filter(predicate)
                        .map(ProviderUtils::buildAssignment)
                        .toArray(String[]::new))
                .WHERE(ProviderUtils.buildAssignment(tableMetadata.getKeyColumn()))
                .toString();
    }

    /**
     * 生成查询单个持久化对象的 Select 语句
     */
    default String get(ProviderContext context) {
        Class<?> clazz = this.getPersistenceType(context);
        TableMetadata tableMetadata = TableMetadata.forClass(clazz);
        return new SQL()
                .SELECT(Stream.of(tableMetadata.getColumns())
                        .map(TableColumn::getName)
                        .toArray(String[]::new))
                .FROM(tableMetadata.getTableName())
                .WHERE(ProviderUtils.buildAssignment(tableMetadata.getKeyColumn()))
                .toString();
    }

    /**
     * 生成查询所有记录的 Select 语句
     */
    default String listAll(ProviderContext context) {
        Class<?> clazz = this.getPersistenceType(context);
        TableMetadata tableMetadata = TableMetadata.forClass(clazz);
        return new SQL()
                .SELECT(Stream.of(tableMetadata.getColumns())
                        .map(TableColumn::getName)
                        .toArray(String[]::new))
                .FROM(tableMetadata.getTableName())
                .toString();
    }

    /**
     * 生成删除单个持久化对象的 Delete 语句
     */
    default String delete(ProviderContext context) {
        Class<?> clazz = this.getPersistenceType(context);
        TableMetadata tableMetadata = TableMetadata.forClass(clazz);
        return new SQL()
                .DELETE_FROM(tableMetadata.getTableName())
                .WHERE(ProviderUtils.buildAssignment(tableMetadata.getKeyColumn()))
                .toString();
    }

    /**
     * 生成逻辑删除单个持久化对象的 Delete 语句
     */
    default String deleteLogical(ProviderContext context) {
        Class<?> clazz = this.getPersistenceType(context);
        TableMetadata tableMetadata = TableMetadata.forClass(clazz);
        return new SQL()
                .UPDATE(tableMetadata.getTableName())
                .SET("is_deleted = 1")
                .WHERE(ProviderUtils.buildAssignment(tableMetadata.getKeyColumn()))
                .toString();
    }

}
